package com.zhdl.hardware_contact.hardware_common.param;

import com.project.common.util.ByteHelper;
import com.project.common.util.CRC16;
import lombok.Data;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 * 组装modbus-Rtu响应数据包
 */
@Data
public class RtuAssembleCmdPkg {
    /**
     * 根据数据单元组装完整响应数据包
     *
     * @param unitBuff 数据内容
     */
    private static byte[] PkgFullFrame(byte[] unitBuff) throws IOException {
        ByteArrayOutputStream bw = new ByteArrayOutputStream();
        // 传输标识符 2HEX
        bw.write((byte) 0x00);
        bw.write((byte) 0x00);
        // 协议标识符 2HEX
        bw.write((byte) 0x00);
        bw.write((byte) 0x00);
        // 长度 2HEX 高字节在前
        short length = (short) (unitBuff.length + 1);
        bw.write((byte) ((length & 0xFF00)) >> 8); // DataNum(H)
        bw.write((byte) (length & 0x00FF)); // DataNum(L)
        // 站标识 目前固定为 0x01
        // 具体指令的数据单元部分
        bw.write(0x01); // DataNum(L)
        bw.write(unitBuff);
        return bw.toByteArray();
    }

    /**
     * 根据数据单元组装完整响应数据包 包含从站id
     *
     * @param unitBuff 完整数据
     * @param id       站标识Id
     */
    private static byte[] PkgFullFrame(byte[] unitBuff, int id) throws IOException {
        ByteArrayOutputStream bw = new ByteArrayOutputStream();
        // 传输标识符 2HEX
        bw.write((byte) 0x00);
        bw.write((byte) 0x00);
        // 协议标识符 2HEX
        bw.write((byte) 0x00);
        bw.write((byte) 0x00);
        // 长度 2HEX 高字节在前
        short length = (short) (unitBuff.length + 1);
        bw.write((byte) ((length & 0xFF00)) >> 8); // DataNum(H)
        bw.write((byte) (length & 0x00FF)); // DataNum(L)
        // 站标识 目前固定为 0x01
        bw.write((byte) id);
        // 具体指令的数据单元部分
        bw.write(unitBuff);
        return bw.toByteArray();
    }

    /**
     * 0x01 读线圈
     */
    public static byte[] read01(CurrencyProtocolReadReq req) throws IOException {
        ByteArrayOutputStream bw = new ByteArrayOutputStream();
        byte[] bsTemp;
        bw.write(ByteHelper.stringToBytes(req.id));
        // 功能码 1HEX
        // 寄存器值 N*2HEX
        bsTemp = (ByteHelper.stringToBytes(req.startAddress));
        bw.write(bsTemp);
        bw.write(ByteHelper.stringToBytes(req.readNumber));
        return PkgFullFrame(bw.toByteArray());
    }

    /**
     * 0x02 读离散量
     */
    public static byte[] read02(CurrencyProtocolReadReq req) throws IOException {
        ByteArrayOutputStream bw = new ByteArrayOutputStream();
        byte[] bsTemp;

        bw.write(ByteHelper.stringToBytes(req.id));
        // 功能码 1HEX

        // 寄存器值 N*2HEX
        bsTemp = (ByteHelper.stringToBytes(req.startAddress));
        bw.write(bsTemp);

        bw.write(ByteHelper.stringToBytes(req.readNumber));

        return PkgFullFrame(bw.toByteArray());
    }

    /**
     * 0x03 读保持寄存器
     */
    public static byte[] read03(CurrencyProtocolReadReq req) throws IOException {
        ByteArrayOutputStream bw = new ByteArrayOutputStream();
        byte[] bsTemp;
        //bw.write(ByteHelper.stringToBytes(req.id));
        //从站id
        bw.write((byte) Integer.parseInt(req.id));
        // 功能码 1HEX
        bw.write((byte) 0x03);
        // 起始地址 2HEX 0x0000 至 0xFFFF
        bsTemp = ByteHelper.stringToBytes(req.startAddress);
        bw.write(bsTemp);
        // 寄存器值
        bsTemp = ByteHelper.stringToBytes(req.readNumber);
        bw.write(bsTemp);
        bsTemp = bw.toByteArray();
        byte[] calcBytes = new byte[bsTemp.length];
        System.arraycopy(bsTemp, 0, calcBytes, 0, calcBytes.length);
        byte[] crc = CRC16.MODBUS(calcBytes);
        bw.write(crc);
        return bw.toByteArray();
    }

    /**
     * 0x04 读输入寄存器
     */
    public static byte[] read04(CurrencyProtocolReadReq req) throws IOException {
        ByteArrayOutputStream bw = new ByteArrayOutputStream();
        // 功能码 1HEX
        bw.write((byte) 0x04);
        // 寄存器值 N*2HEX
        byte[] bsTemp = (ByteHelper.stringToBytes(req.startAddress));
        bw.write(bsTemp);
        return PkgFullFrame(bw.toByteArray());
    }

    /**
     * 0x05 写线圈
     */
    public static byte[] write05(CurrencyProtocolWriteReq req) throws IOException {
        ByteArrayOutputStream bw = new ByteArrayOutputStream();
        byte[] bsTemp;
        // 功能码 1HEX
        bw.write((byte) 0x05);

        // 寄存器值 N*2HEX
        bsTemp = (ByteHelper.stringToBytes(req.startAddress));
        bw.write(bsTemp);
        bsTemp = (ByteHelper.stringToBytes(req.writeValue));
        bw.write(bsTemp);
        int i = Integer.parseInt(req.id);
        return PkgFullFrame(bw.toByteArray(), i);
    }

    /**
     * 0x06 写单个寄存器
     */
    public static byte[] write06(CurrencyProtocolWriteReq req) throws IOException {
        ByteArrayOutputStream bw = new ByteArrayOutputStream();
        byte[] bsTemp;
        bw.write((byte) Integer.parseInt(req.id));
        // 功能码 1HEX
        bw.write((byte) 0x06);
        //单个线圈寄存器地址 0x3500
        bsTemp = ByteHelper.hexString2Bytes(req.startAddress);
        bw.write(bsTemp);
        //修改寄存器的值位 00 06
        bsTemp = ByteHelper.hexString2Bytes(req.writeValue);
        bw.write(bsTemp);
        bsTemp = bw.toByteArray();
        byte[] calcBytes = new byte[bsTemp.length];
        System.arraycopy(bsTemp, 0, calcBytes, 0, calcBytes.length);
        byte[] crc = CRC16.MODBUS(calcBytes);
        bw.write(crc);
        return bw.toByteArray();
    }

    /**
     * 0x10 写多个连续保持寄存器
     */
    public static byte[] write10(CurrencyProtocolWriteReq req) throws IOException {
        ByteArrayOutputStream bw = new ByteArrayOutputStream();
        byte[] bsTemp;
        bw.write((byte) Integer.parseInt(req.id));
        // 功能码 1HEX
        bw.write((byte) 0x10);
        // 起始地址 2HEX 0x0000 至 0xFFFF
        bsTemp = ByteHelper.hexString2Bytes(req.startAddress);
        bw.write(bsTemp);
        short word = (short) (req.writeValue.length() / 4);
        bw.write(ByteHelper.shortToByteArrayByHigh(word));
        //数据长度
        bw.write(req.writeValue.length() / 2);
        // 寄存器值
        bsTemp = ByteHelper.hexString2Bytes(req.writeValue);
        bw.write(bsTemp);
        bsTemp = bw.toByteArray();
        byte[] calcBytes = new byte[bsTemp.length];
        System.arraycopy(bsTemp, 0, calcBytes, 0, calcBytes.length);
        byte[] crc = CRC16.MODBUS(calcBytes);
        bw.write(crc);
        return bw.toByteArray();
    }

}
